﻿/*
 * Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved.
 */

#include "BurnDlgDfu.h"
extern "C" {
#include <hidsdi.h>
};
#include <QMessageBox>
#include <QSettings>
#include <QFileDialog>
#include <QTimer>
#include <QTranslator>
#include <QScrollBar>
#include <cfgmgr32.h>
#include <initguid.h>
#include <usbioctl.h>
#include "BurnToolCommon/Global.h"
#include "BurnCtrl/BurnCtrlFaction.h"
#include "DataConvertor.h"
#include "DiaglogComSetting.h"
#include "MultipleBurnDlg.h"
#include "SecureLink/Channel.h"
using namespace BurnDlg;

namespace {
    const int COLUMN_WIDTH_SIZE_20 = 20;
    const int COLUMN_WIDTH_SIZE_100 = 100;
    const int COLUMN_WIDTH_SIZE_200 = 200;
    const int COLUMN_WIDTH_SIZE_300 = 300;
    const int COLUMN_WIDTH_SIZE_500 = 500;
    const int COLUMN_MINIMUM_HEIGHT_SIZE_192 = 192;
    const int COLUMN_MINIMUM_HEIGHT_SIZE_400 = 400;
    const int MAX_BLOCK_COUNT_1000 = 1000;
    const int MAX_STRING_WCHARS = 256;
    const int MULTIPLE_TOTAL_NUM = 8;
    const int MULTIPLE_NUM_PER_LINE = 8;
}
using namespace BurnDlg;

BurnDlgDfu::BurnDlgDfu(ChipType chipType, QWidget *pParent)
    : BurnDlg(pParent),
      ui(new Ui::BurnDlgDfu),
      actImportConfig(new QAction(stringClass.GetActionImportConfig())),
      actSaveConfig(new QAction(stringClass.GetActionSaveConfig())),
      actSettings(new QAction(stringClass.GetActionUSBSettings())),
      actEnglish(new QAction("English")),
      actChinese(new QAction("中文")),
      actChangeChip(new QAction(stringClass.GetChangeChip())),
      actAbout(new QAction(stringClass.GetAbout())),
      isDfuModelCtrl(false),
      dfuMode(DfuBurnMode::DFU),
      cfg(nullptr),
      isMultipleBurnOpen(false),
      multipleBurnDlg(nullptr),
      eraseMode(0),
      usbSetting(new DiaglogUSBSetting())
{
    QString fileName = QCoreApplication::applicationDirPath() + "\\configure\\config_dfu\\config.ini";
    cfg = new QSettings(fileName, QSettings::IniFormat);
    BurnDlg::chipType = chipType;
    burnCtrl = nullptr;
    if (ui == nullptr) {
        return;
    }
    ui->setupUi(this);
    if (actEnglish != nullptr) {
        actEnglish->setCheckable(true);
        actEnglish->setChecked(true);
        actEnglish->setEnabled(true);
    }
    if (actChinese != nullptr) {
        actChinese->setCheckable(false);
        actChinese->setChecked(false);
        actChinese->setEnabled(true);
    }
    if (chipType == ChipType::USB4GCAT1 || chipType == ChipType::USBHI3322) {
        ui->menuSetting->show();
        if (actSettings != nullptr) {
            ui->menuSetting->addAction(actSettings);
        }
    }
    ui->menuSetting->addMenu(menuWifiLanguage);

    if (actChangeChip != nullptr) {
        ui->menuOption->addAction(actChangeChip);
    }
    binModel = new BinFileModel();
    if (binModel != nullptr) {
        ui->tableView->setModel(binModel);
    }
    if (actAbout != nullptr) {
        ui->menuHelp->addAction(actAbout);
    }
    InitializeInterfaceControls();
    SlotDfuModeCurrentTextChanged(ui->cbDfuMode->currentText());
}

BurnDlgDfu::~BurnDlgDfu()
{
    if (ui != nullptr) {
        delete ui;
    }

    if (actImportConfig != nullptr) {
        delete actImportConfig;
        actImportConfig = nullptr;
    }

    if (actSaveConfig != nullptr) {
        delete actSaveConfig;
        actSaveConfig = nullptr;
    }

    if (actSettings != nullptr) {
        delete actSettings;
        actSettings = nullptr;
    }

    if (actEnglish != nullptr) {
        delete actEnglish;
        actEnglish = nullptr;
    }

    if (actChinese != nullptr) {
        delete actChinese;
        actChinese = nullptr;
    }

    if (actChangeChip != nullptr) {
        delete actChangeChip;
        actChangeChip = nullptr;
    }

    if (actAbout != nullptr) {
        delete actAbout;
        actAbout = nullptr;
    }

    BurnDlgFuDelete();
}

void BurnDlgDfu::BurnDlgFuDelete()
{
    if (binModel != nullptr) {
        delete binModel;
        binModel = nullptr;
    }

    if (cfg != nullptr) {
        delete cfg;
        cfg = nullptr;
    }
    if (encryptBuffer.bufferData != nullptr) {
        delete [] encryptBuffer.bufferData;
        encryptBuffer.bufferData = nullptr;
    }
    encryptBuffer.bufferLen = 0;

    if (multipleBurnDlg != nullptr) {
        delete multipleBurnDlg;
        multipleBurnDlg = nullptr;
    }

    if (usbSetting != nullptr) {
        delete usbSetting;
        usbSetting = nullptr;
    }
}

void BurnDlgDfu::SlotDfuSaveConfig() const
{
    if (cfg != nullptr) {
        cfg->setValue("DFU_CONFIG/AUTO_DFU", ui->cbDfuMode->currentText());
        cfg->setValue("DFU_CONFIG/VID", ui->leVid->text());
        cfg->setValue("DFU_CONFIG/PID", ui->lePid->text());
    }
}

void BurnDlgDfu::InitializeInterfaceControls()
{
    if (ui == nullptr || binModel == nullptr) {
        return;
    }
    // 界面控件
    ui->comboBoxCom->setEditable(true);
    ui->textEditReceive->setReadOnly(true);
    ui->textEditReceive->document()->setMaximumBlockCount(MAX_BLOCK_COUNT_1000);
    ui->textEditReceive->setTextInteractionFlags(Qt::NoTextInteraction);
    ui->progressBarReceive->setRange(0, SHRT_MAX);
    ui->progressBarReceive->setValue(0);
    if (chipType == ChipType::USBBS27) {
        ui->cbDfuMode->addItems(QStringList() << "Hid dfu");
    } else {
        ui->cbDfuMode->addItems(QStringList() << "Dfu" << "Auto dfu" << "Hid dfu");
    }

    if (cfg != nullptr) {
        ui->cbDfuMode->setCurrentText((cfg->value("DFU_CONFIG/AUTO_DFU").toString()));
        ui->leVid->setText(cfg->value("DFU_CONFIG/VID").toString());
        ui->lePid->setText(cfg->value("DFU_CONFIG/PID").toString());
    }
    isDfuModelCtrl = false;
    InitializeBurnCtrl();
    InitializeInterfaceControlsAdd();
    InitSettingsConnect();
    InitButtonConnect();
    ui->tableView->setSelectionMode(QAbstractItemView::SingleSelection);
    ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
    ui->tableView->setDefaultDropAction(Qt::MoveAction);
    ui->tableView->setDragDropMode(QAbstractItemView::InternalMove);
    ui->tableView->setColumnWidth(TABLEVIEW_COLUMN_CHECK, COLUMN_WIDTH_SIZE_20);
    ui->tableView->setColumnWidth(TABLEVIEW_COLUMN_ADD, COLUMN_WIDTH_SIZE_20);
    ui->tableView->setColumnWidth(TABLEVIEW_COLUMN_DELETE, COLUMN_WIDTH_SIZE_20);
    ui->tableView->setColumnWidth(TABLEVIEW_COLUMN_NAME, COLUMN_WIDTH_SIZE_100);
    ui->tableView->setColumnWidth(TABLEVIEW_COLUMN_PAHT, COLUMN_WIDTH_SIZE_200);
    ui->tableView->setMinimumHeight(COLUMN_MINIMUM_HEIGHT_SIZE_192);
    ui->tableView->horizontalHeader()->setSectionResizeMode(TABLEVIEW_COLUMN_CHECK, QHeaderView::Fixed);
    ui->tableView->horizontalHeader()->setSectionResizeMode(TABLEVIEW_COLUMN_ADD, QHeaderView::Fixed);
    ui->tableView->horizontalHeader()->setSectionResizeMode(TABLEVIEW_COLUMN_DELETE, QHeaderView::Fixed);
    InitMenus();
    if (Global::IsUSBType(chipType)) {
        ui->labelCom->setText("USB: ");
    }
    SlotCbnSetfocusComboCom();
    connect(ui->comboBoxCom, &ComboBoxCom::Clicked, this, &BurnDlgDfu::SlotCbnSetfocusComboCom);
    connect(binModel, &BinFileModel::dataChanged, this, &BurnDlgDfu::SlotLvnItemchangedListBinPath);
    connect(binModel, &QAbstractItemModel::modelReset, this, &BurnDlgDfu::SlotModelReset);
}

void BurnDlgDfu::InitializeInterfaceControlsAdd()
{
    if (chipType == ChipType::USB4GCAT1 || chipType == ChipType::USBHI3322) {
        ui->cbEarseMode->show();
        ui->cbEarseMode->setEditable(true);

        ui->cbEarseMode->addItem(stringClass.GetNormal());
        ui->cbEarseMode->addItem(stringClass.GetEraseAll());
        ui->cbEarseMode->addItem(stringClass.GetNoErase());
        if (cfg != nullptr) {
            int index = ui->cbEarseMode->findText(cfg->value("DFU_CONFIG/ERASE_MODE").toString());
            if (index != -1 && burnCtrl != nullptr) {
                ui->cbEarseMode->setCurrentIndex(index);
                eraseMode = index;
                burnCtrl->SetEraseMode(eraseMode);
            }
        }
        ui->cbDfuMode->setCurrentText("Auto dfu");
        ui->cbDfuMode->hide();
        ui->lbDfuMode->hide();
        ui->horizontalSpacer_3->changeSize(0, 0);
        ui->gridLayout_2->update();
        ui->gridLayout_2->setEnabled(false);
        ui->btnMultipleBurn->show();

        this->LoadDlgConfig();
    } else {
        ui->cbEarseMode->hide();
        ui->lbEraseMode->hide();
        ui->btnMultipleBurn->hide();
    }
}

void BurnDlgDfu::InitMenus()
{
    this->tableViewMenu = new QMenu(this);
    QAction *createNewFileAction = new QAction(stringClass.GetCreateNewFile());
    if (createNewFileAction != nullptr) {
        this->tableViewMenu->addAction(createNewFileAction);
        connect(createNewFileAction, &QAction::triggered, this, &BurnDlgDfu::SlotCreateNewFwpkgFile);
    }
    QAction *saveSpiltFileAction = new QAction(stringClass.GetSaveSplitFile());
    if (saveSpiltFileAction != nullptr) {
        this->tableViewMenu->addAction(saveSpiltFileAction);
        connect(saveSpiltFileAction, &QAction::triggered, this, &BurnDlgDfu::SlotSaveSpiltFwpkgFile);
    }
}

void BurnDlgDfu::contextMenuEvent(QContextMenuEvent *event)
{
    Q_UNUSED(event);
    if (!ui->tableView->selectionModel()->currentIndex().isValid()) {
        return;
    }
    this->tableViewMenu->exec(QCursor::pos());
}

void BurnDlgDfu::SlotCreateNewFwpkgFile()
{
    CreateNewFwpkgFile(ui->lineEditSeleteFile->text());
}

void BurnDlgDfu::SlotSaveSpiltFwpkgFile()
{
    SaveSpiltFwpkgFile(ui->lineEditSeleteFile->text());
}

void BurnDlgDfu::InitializeBurnCtrl()
{
    this->BurnDlgDfu::InitBurnCtrl();
}

void BurnDlgDfu::InitBurnCtrlConnect()
{
    if (burnCtrl == nullptr) {
        return;
    }
    connect(burnCtrl, &BurnCtrl::SignalInsertPlainText, this, &BurnDlgDfu::SlotInsertPlainText);
    connect(burnCtrl, &BurnCtrl::SignalClearPlainText, this, &BurnDlgDfu::SlotClearPlainText);
    connect(burnCtrl, &BurnCtrl::SignalReplacePlainText, this, &BurnDlgDfu::SlotReplacePlainText);
    connect(burnCtrl, &BurnCtrl::SignalSwitchDfu, this, &BurnDlgDfu::SlotSwitchDfu);
    connect(burnCtrl, &BurnCtrl::SignalAutoDisconnect, this, &BurnDlgDfu::SlotAutoDisconnect);
    connect(burnCtrl, &BurnCtrl::SignalMergeCurrentCharFormat, this, &BurnDlgDfu::SlotMergeCurrentCharFormat);
    connect(burnCtrl, &BurnCtrl::SignalSetProgressValue, this, &BurnDlgDfu::SlotSetProgressValue);
    connect(burnCtrl, &BurnCtrl::SignalSetProgressRange, this, &BurnDlgDfu::SlotSetProgressRange);
    connect(burnCtrl, &BurnCtrl::SignalBurnFileFinish, this, &BurnDlgDfu::SlotBurnFileFinish);
}

void BurnDlgDfu::InitSettingsConnect()
{
    // setting
    if (actSettings != nullptr) {
        connect(actSettings, &QAction::triggered, this, &BurnDlgDfu::SlotShowMenuSettings);
    }
    if (actChangeChip != nullptr) {
        connect(actChangeChip, &QAction::triggered, this, &BurnDlgDfu::SlotShowChangeChip);
    }
    if (actAbout != nullptr) {
    // help
        connect(actAbout, &QAction::triggered, this, &BurnDlgDfu::SlotShowAbout);
    }
}

void BurnDlgDfu::InitButtonConnect()
{
    connect(ui->btnSelectFile, &QPushButton::clicked, this, &BurnDlgDfu::SlotBnClickedButtonSelectfile);
    connect(ui->btnStartBurn, &QPushButton::clicked, this, &BurnDlgDfu::SlotBnClickedButtonConn);
    connect(ui->tableView->horizontalHeader(), &QHeaderView::sectionClicked, this,
            &BurnDlgDfu::SlotBnClickedTableHeader);
    connect(ui->tableView, &QTableView::clicked, this, &BurnDlgDfu::SlotBnClickedTableView);
    connect(ui->tableView, &QTableView::doubleClicked, this, &BurnDlgDfu::SlotBnDoubleClickedTableView);
    connect(ui->cbDfuMode, &QComboBox::currentTextChanged, this, &BurnDlgDfu::SlotDfuModeCurrentTextChanged);

    connect(ui->lePid, &QLineEdit::textChanged, this, &BurnDlgDfu::SlotDfuSaveConfig);
    connect(ui->leVid, &QLineEdit::textChanged, this, &BurnDlgDfu::SlotDfuSaveConfig);
    connect(ui->btnMultipleBurn, &QPushButton::clicked, this, &BurnDlgDfu::SlotBnClickedMultipleBurn);
    connect(ui->cbEarseMode, SIGNAL(currentIndexChanged(int)), this, SLOT(SlotEarseModeCurrentIndexChanged(int)));
}

void BurnDlgDfu::SlotShowMenuSettings()
{
    if (usbSetting != nullptr) {
        connect(usbSetting, &DiaglogUSBSetting::SignalSettingSuccess, this, &BurnDlgDfu::SlotSettingSuccess);
        usbSetting->show();
    }
}

void BurnDlgDfu::SlotSettingSuccess()
{
    if (burnCtrl == nullptr) {
        return;
    }

    if (usbSetting != nullptr) {
        unsigned int totalNum;
        unsigned int numPerLine;
        usbSetting->GetMultipleBurnParam(totalNum, numPerLine);
        burnCtrl->SetMultiBurnTotalNum(totalNum);
        burnCtrl->SetMultiBurnNumPerLine(numPerLine);
    }
    SaveSettingCfg();
}

void BurnDlgDfu::InitBurnCtrl()
{
    BurnCtrlFaction faction;
    static bool lastIsDfuModelCtrl = false;
    if (lastIsDfuModelCtrl == isDfuModelCtrl && burnCtrl != nullptr) {
        return;
    } else {
        lastIsDfuModelCtrl = isDfuModelCtrl;
    }
    if (burnCtrl != nullptr) {
        delete burnCtrl;
        burnCtrl = nullptr;
    }
    if (!isDfuModelCtrl) {
        burnCtrl = faction.GetBurnCtrl(chipType);
    } else {
        burnCtrl = faction.GetBurnCtrl(ChipType::DFU);
    }
    if (burnCtrl == nullptr) {
        return;
    }
    burnCtrl->SetBurnDlg(this);
    burnCtrl->StartWiteMonitoring();
    InitBurnCtrlConnect();
}

void BurnDlgDfu::GetComSettingFromGloble(int &baud, int &databits, int &stopbits,
    QSerialPort::Parity &parity, int &flowctrl) const
{
    SEBOOT_SHAKE_HANDS_FRAME *head = reinterpret_cast<SEBOOT_SHAKE_HANDS_FRAME *>(Global::shakeHandSignal);
    baud = head->baudRate;
    databits = head->dataBits;
    stopbits = head->stopBits;
    if (head->parity == 0) {
        parity = QSerialPort::NoParity;
    } else if (head->parity == 1) {
        parity = QSerialPort::OddParity;
    } else {
        parity = QSerialPort::EvenParity;
    }
    flowctrl = head->flowctrl;
}

void BurnDlgDfu::SetComSettingToGloble(int baud, int databits, int stopbits, QSerialPort::Parity parity,
    int flowctrl) const
{
    SEBOOT_SHAKE_HANDS_FRAME *head = reinterpret_cast<SEBOOT_SHAKE_HANDS_FRAME *>(Global::shakeHandSignal);
    head->baudRate = baud;
    head->dataBits = databits;
    head->stopBits = stopbits;
    if (parity == QSerialPort::EvenParity) {
        head->parity = 2; // even check trans to 2
    } else if (parity == QSerialPort::OddParity) {
        head->parity = 1;
    } else {
        head->parity = 0;
    }
    head->flowctrl = flowctrl;
    head->checkSum = ChannelClass::Channel::SopCrc16(static_cast<PBYTE>(Global::shakeHandSignal),
        SHAKE_HAND_SIGNAL_LEN - 2); // crc16 is 2
}

void BurnDlgDfu::SlotBnClickedButtonSelectfile()
{
    QString filePath = QFileDialog::getOpenFileName(this, tr("Select File"), "", stringClass.GetIdsString32941());
    if (!filePath.isEmpty()) {
        AnalyseBinFile(filePath);
    }
}

void BurnDlgDfu::ResetUIAndFSM()
{
    BurnDlg::ResetUIAndFSM();
    ui->btnStartBurn->setText(stringClass.GetStartBurn());
    ui->progressBarReceive->setValue(0);
}

void BurnDlgDfu::SlotBnClickedButtonConn()
{
    Global::mUsbSerialLists.clear();
    isDfuModelCtrl = false;
    InitializeBurnCtrl();
    StartBurn();
}

void BurnDlgDfu::StartBurn()
{
    if (burnCtrl == nullptr) {
        return;
    }
    burnCtrl->SetIsCancel(FALSE);
    burnCtrl->SetPortType(portType);
    if (burnCtrl->IsComOpen()) {
        ResetUIAndFSM();
        burnCtrl->SetIsCancel(TRUE);
        return;
    }
    if (!QFile(ui->lineEditSeleteFile->text()).exists()) {
        QMessageBox::warning(nullptr, MESSAGE_BURN_TOOL, stringClass.GetNoValidFlashFile());
        ui->btnStartBurn->setText(stringClass.GetStartBurn());
        burnCtrl->SetIsCancel(TRUE);
        return;
    }
    UpdateList();
    burnCtrl->UpdateComSetting(OPEN_SERIAL_PORT_DEFAULT_BAUD_RATE, OPEN_SERIAL_PORT_DEFAULT_DATA_BIT,
                               OPEN_SERIAL_PORT_DEFAULT_STOP_BIT, QSerialPort::NoParity,
                               OPEN_SERIAL_PORT_DEFAULT_FLOWCTRL);
    burnCtrl->SetIsAutoDfu(dfuMode);
    if (!Global::IsUSBType(chipType) && !ConfigComPort()) {
        return;
    } else if (Global::IsUSBType(chipType)) {
        if (dfuMode == DfuBurnMode::DFU) {
            QString usbDevice = ui->comboBoxCom->currentText();
            QStringList usbDeviceList = usbDevice.split(' ');
            if (!usbDeviceList.isEmpty()) {
                usbDevice = usbDeviceList.back().remove('(');
                usbDevice = usbDevice.remove(')');
            }
            burnCtrl->SetUsbDeviceInfo(hidShowInfoMap[ui->comboBoxCom->currentText()]);
        } else {
            HidDeviceInfo hidInfo;
            bool ok = false;
            hidInfo.vid = ui->leVid->text().toUInt(&ok, 0x10);
            hidInfo.pid = ui->lePid->text().toUInt(&ok, 0x10);
            burnCtrl->SetUsbDeviceInfo(hidInfo);
        }
    }
    burnCtrl->SetAutoBurnFlag(true);
    burnCtrl->SetLoaderDfu(true);
    SlotClearPlainText();
    if (dfuMode != DfuBurnMode::AUTO_DFU) {
        SlotInsertPlainText(stringClass.GetIdsString32897());
    }
    emit SignalSendMsgInBurnDlg(static_cast<long>(WiteThreadMsg::WITE_THREAD_MSG_STARTCONNECT), 0, 0);
    emit SignalSendMsgInBurnDlg(static_cast<long>(WiteThreadMsg::WITE_THREAD_MSG_INITBURNCMD), 0, 0);
    ui->btnStartBurn->setText(stringClass.GetStopBurn());
    return;
}

void BurnDlgDfu::UpdateCmd() const
{
    if (burnCtrl == nullptr || binModel == nullptr) {
        return;
    }
    int nRow = binModel->rowCount();
    if (nRow > 0x1000) {
        return;
    }
    QMap<int, BIN_INFO> infoArray;
    GetBinInforArray(infoArray, nRow);
    if (burnCtrl != nullptr) {
        burnCtrl->InitBurnCmdByArray(infoArray);
    }
}

void BurnDlgDfu::GetCmdInfoArray(QMap<int, BIN_INFO> &infoArray) const
{
    if (binModel == nullptr) {
        return;
    }
    int nRow = binModel->rowCount();
    if (nRow > 0x1000) {
        return;
    }
    infoArray.clear();

    GetBinInforArray(infoArray, nRow);
}

void BurnDlgDfu::UpdateList()
{
    if (binModel == nullptr) {
        return;
    }
    int nRow = binModel->rowCount();
    if (nRow > 0x1000) {
        return;
    }
    std::vector<QString> pathArray;

    for (int i = 0; i < nRow; i++) {
        if (binModel->GetCheck(i)) {
            QString path = binModel->GetValue(i, TABLEVIEW_COLUMN_PAHT);
            pathArray.push_back(path);
        }
    }
    std::vector<QString> result;
    for (auto item : pathArray) {
        bool needAddWifi = true;
        for (auto item2 : result) {
            if (item2 == item) {
                needAddWifi = false;
                break;
            }
        }
        if (needAddWifi) {
            result.push_back(item);
        }
    }
    U32 needInitCmd = 0;
    QString version;
    for (auto item : result) {
        BIN_INFO_ARRAY infoArray;
        DataConvertor::AnalysMultifBin(item, infoArray, version, isEncrypt, encryptBuffer);
        needInitCmd |= static_cast<unsigned int>(UpdateList(infoArray));
    }
    if (needInitCmd != 0) {
        UpdateCmd();
    }
}

bool BurnDlgDfu::UpdateList(BIN_INFO_ARRAY &info) const
{
    if (binModel == nullptr) {
        return false;
    }
    bool needInitCmd = false;
    for (int i = 0; i < info.GetSize(); i++) {
        BIN_INFO item = info.GetAt(i);
        int colNum = binModel->rowCount(); // 取行数
        if (colNum > 0x1000) {
            break;
        }
        QMap<int, BIN_INFO> mapBinInfo;
        mapBinInfo.clear();
        mapBinInfo = binModel->GetMapBinFileInfo();
        for (int j = 0; j < colNum; j++) {
            if ((!item.fileName.isEmpty()) && binModel->GetCheck(j) &&
                 item.path == mapBinInfo[j].path && item.fileName == mapBinInfo[j].fileName &&
                 item.type == mapBinInfo[j].type && (mapBinInfo[j].fileIndex != item.fileIndex ||
                 mapBinInfo[j].fileSize != item.fileSize || mapBinInfo[j].burnAddr != item.burnAddr ||
                 mapBinInfo[j].burnSize != item.burnSize)) {
                mapBinInfo[j].fileIndex = item.fileIndex;
                mapBinInfo[j].fileSize = item.fileSize;
                mapBinInfo[j].burnAddr = item.burnAddr;
                mapBinInfo[j].burnSize = item.burnSize;
                needInitCmd = true;
            } else if (item.fileName.isEmpty() && binModel->GetCheck(j) && (item.path == mapBinInfo[j].path) &&
                       (item.fileName == mapBinInfo[j].fileName) && (item.fileSize != mapBinInfo[j].fileSize)) {
                mapBinInfo[j].fileSize = item.fileSize;
                needInitCmd = true;
            }
        }
        binModel->FetchData(mapBinInfo);
    }
    return needInitCmd;
}

void BurnDlgDfu::AnalyseBinFile(QString filePath)
{
    if (burnCtrl == nullptr || burnCtrl->IsBusy() || binModel == nullptr || ui == nullptr) {
        QMessageBox::critical(this, stringClass.GetMessageError(),
                              stringClass.GetIdsString32984(), stringClass.GetYes());
        return;
    }
    ui->lineEditSeleteFile->setText(filePath);
    binModel->ClearData();
    QMap<int, BIN_INFO> mapBinInfo = binModel->GetMapBinFileInfo();
    BIN_INFO_ARRAY infoArray;
    QString version;
    DataConvertor::AnalysMultifBin(ui->lineEditSeleteFile->text(), infoArray, version,
                                   isEncrypt, encryptBuffer);
    int startImport = mapBinInfo.size();
    for (int i = 0; i < infoArray.GetSize(); i++) {
        mapBinInfo[startImport + i] = infoArray.GetAt(i);
    }
    QMap<int, bool> mapIsCheck;
    for (int j = 0; j < mapBinInfo.size(); j++) {
        // 4GCAT1 USB烧写模式下erase all场景默认全选所有文件
        if (chipType == ChipType::USB4GCAT1 && eraseMode == 1) {
            mapIsCheck[j] = true;
        } else {
            if (mapBinInfo[j].type == IMAGE_TYPE::IMAGE_TYPE_RADIOCONFIG ||
                mapBinInfo[j].type == IMAGE_TYPE::IMAGE_TYPE_FLASHBOOT_3892 ||
                IsSelectOtp(mapBinInfo[j].type)) {
                mapIsCheck[j] = false;
            } else {
                mapIsCheck[j] = true;
            }
        }
    }
    binModel->FetchData(mapIsCheck);
    binModel->FetchData(mapBinInfo);
}

void BurnDlgDfu::SlotNMKillfocusListBinPath(const NMHDR *pNMHDR, LRESULT *pResult)
{
    Q_UNUSED(pNMHDR)
    if (burnCtrl == nullptr || burnCtrl->IsBusy() || pResult == nullptr || binModel == nullptr) {
        return;
    }
    UpdateList();
    int nRow = binModel->rowCount();
    if (nRow > 0x1000) {
        return;
    }
    QMap<int, BIN_INFO> infoArray;

    GetBinInforArray(infoArray, nRow);
    burnCtrl->InitBurnCmdByArray(infoArray);
    *pResult = 0;
}

void BurnDlgDfu::SlotLvnItemchangedListBinPath(const QModelIndex &topLeft, const QModelIndex &bottomRight,
                                               const QVector<int> &roles)
{
    Q_UNUSED(topLeft);
    Q_UNUSED(bottomRight);
    Q_UNUSED(roles);

    if (burnCtrl == nullptr) {
        return;
    }
    if (burnCtrl->IsBusy()) {
        return;
    }
    UpdateList();
    UpdateCmd();
    JudgeIsSelectAll();
}

void BurnDlgDfu::SlotModelReset() const
{
    if (burnCtrl == nullptr) {
        return;
    }
    if (burnCtrl->IsBusy()) {
        return;
    }

    UpdateCmd();
    JudgeIsSelectAll();
}

void BurnDlgDfu::SlotBnClickedTableHeader(int logicalIndex)
{
    if (burnCtrl == nullptr || burnCtrl->IsBusy()) {
        QMessageBox::critical(this, stringClass.GetMessageError(),
                              stringClass.GetIdsString32984(), stringClass.GetYes());
        return;
    }
    BurnDlg::SlotBnClickedTableHeader(logicalIndex);

    UpdateCmd();
}

void BurnDlgDfu::JudgeIsSelectAll() const
{
    if (binModel == nullptr) {
        return;
    }
    BOOL setcheck = TRUE;
    int nRow = binModel->rowCount();
    if (nRow > 0x1000) {
        return;
    }
    for (int i = 0; i < nRow; i++) {
        setcheck = static_cast<BOOL>(static_cast<UINT>(setcheck) & static_cast<UINT>(binModel->GetCheck(i)));
    }
}

void BurnDlgDfu::GetBinInforArray(QMap<int, BIN_INFO> &infoArray, int row) const
{
    if (binModel == nullptr) {
        return;
    }
    for (int i = 0, j = 0; i < row; i++) {
        if (binModel->GetCheck(i)) {
            BIN_INFO info;
            info.fileName = binModel->GetValue(i, TABLEVIEW_COLUMN_NAME);
            info.path = binModel->GetValue(i, TABLEVIEW_COLUMN_PAHT);
            info.fileIndex = binModel->GetValue(i, TABLEVIEW_COLUMN_FILE_INDEX);
            info.fileSize = binModel->GetValue(i, TABLEVIEW_COLUMN_FILE_SIZE);
            info.burnAddr = binModel->GetValue(i, TABLEVIEW_COLUMN_BURN_ADDR);
            info.burnSize = binModel->GetValue(i, TABLEVIEW_COLUMN_BURN_SIZE);
            info.type = binModel->GetType(i);
            infoArray[j++] = info;
        }
    }
}

void BurnDlgDfu::GetAllPathFromGrid(std::vector<QString> &path) const
{
    if (binModel == nullptr) {
        return;
    }
    for (int i = 0; i < binModel->rowCount(); i++) {
        if (binModel->GetCheck(i)) {
            path.push_back(binModel->GetValue(i, TABLEVIEW_COLUMN_PAHT));
        }
    }
}

bool BurnDlgDfu::CheckAllPathInGrid(bool onlyNotify)
{
    std::vector<QString> path;
    GetAllPathFromGrid(path);
    return CheckAllPath(path, onlyNotify);
}

bool BurnDlgDfu::CheckAllPath(std::vector<QString> &path, bool onlyNotify)
{
    bool ret = true;
    std::vector<QString> result;
    if (!DataConvertor::AllPathFileExists(path, result)) {
        QString log = stringClass.GetIdsString33270();
        for (auto item : result) {
            log += item;
            log += "\n";
        }
        if (onlyNotify) {
            if (QMessageBox::warning(this, stringClass.GetMessageError(),
                                     log, stringClass.GetOk(),
                                     stringClass.GetCancel()) == 1) {
                ret = false;
            }
        } else {
            QMessageBox::warning(this, stringClass.GetMessageError(), log);
        }
    }
    return ret;
}

void BurnDlgDfu::SlotCbnSetfocusComboCom()
{
    if (portType == Base::TCP_PORT) {
        return;
    }
    ui->comboBoxCom->clear();
    hidShowInfoMap.clear();
    if (Global::IsUSBType(chipType)) {
        QStringList name;
        QStringList info;
        QString error;
        QStringList strListPortWifi;
        QMap <QString, HidDeviceInfo> mapName;
        mapName.clear();
        HidEnumerate(mapName);
        QStringList hidName;
        for (auto it = mapName.begin(); it != mapName.end(); it++) {
            QString showStr = it.value().productName + " (vid: 0x" + QString::number(it.value().vid, 16) +
                              ", pid: 0x" + QString::number(it.value().pid, 16) +
                              ", usage: 0x" + QString::number(it.value().usage, 16) +
                              ", usage page: 0x" + QString::number(it.value().usagePage, 16) + ")";
            hidName.push_back(showStr);
            hidShowInfoMap[showStr] = it.value();
        }
        ui->comboBoxCom->addItems(hidName);
    } else {
        QString strPort = ui->comboBoxCom->currentText();
        QStringList strListPortWifi;
        DataConvertor::RefreshComList(strListPortWifi);
        ui->comboBoxCom->addItems(strListPortWifi);
        for (int i = 0; i < strListPortWifi.size(); i++) {
            if (strListPortWifi.at(i) == strPort) {
                ui->comboBoxCom->setCurrentText(strPort);
            }
        }
    }
}

LRESULT BurnDlgDfu::SlotSwitchDfu(WPARAM wParam, LPARAM lParam)
{
    if (burnCtrl->IsComOpen() && wParam == 0 && lParam == 0) {
        burnCtrl->SwitchToDfu();
        portType = Base::DFU_PORT;
        SlotInsertPlainText(stringClass.GetIdsString32895());
        isDfuModelCtrl = true;
        InitializeBurnCtrl();
        // 清空网口，刷新串口
        Sleep(500); // 500: sleeptime
        StartBurn();
    }
    return 0;
}

bool BurnDlgDfu::ConfigComPort()
{
    QString strPort = ui->comboBoxCom->currentText();
    if (portType == Base::TCP_PORT) {
        burnCtrl->ConfigTcpPort();
        return true;
    }
    if (portType == Base::DFU_PORT) {
        return true;
    }

    if (ui->comboBoxCom->findText(strPort) != -1) {
        ui->comboBoxCom->setCurrentText(strPort);
    }
    if (ui->comboBoxCom->count() == 0) {
        QMessageBox::warning(this, "", stringClass.GetIdsString32899());
        return false;
    } else {
        int com = strPort.mid(3).toInt();
        if (burnCtrl != nullptr) {
            burnCtrl->SetComNum(com);
            return burnCtrl->ConfigComPort();
        }
        return false;
    }
}

void BurnDlgDfu::SlotInsertPlainText(const QString &editStr)
{
    QScrollBar *scrollbar = ui->textEditReceive->verticalScrollBar();

    bool scrollbarAtButtom = (scrollbar->value() >= scrollbar->maximum() - 50);
    int scrollbarPrevValue = scrollbar->value();
    QTextCursor cursor = ui->textEditReceive->textCursor();
    cursor.movePosition(QTextCursor::End);
    ui->textEditReceive->setTextCursor(cursor);
    ui->textEditReceive->insertPlainText(editStr);
    if (scrollbarAtButtom) {
        ui->textEditReceive->ensureCursorVisible();
    } else {
        ui->textEditReceive->verticalScrollBar()->setValue(scrollbarPrevValue);
    }
}

void BurnDlgDfu::SlotClearPlainText()
{
    ui->textEditReceive->clear();
}

void BurnDlgDfu::SlotReplacePlainText(const QString &oldText, const QString &newText)
{
    QString str = ui->textEditReceive->toPlainText();
    SlotClearPlainText();
    str.replace(oldText, newText);
    SlotInsertPlainText(str);
}

void BurnDlgDfu::SlotMergeCurrentCharFormat(const QTextCharFormat &fmt)
{
    ui->textEditReceive->mergeCurrentCharFormat(fmt);
}

void BurnDlgDfu::SlotSetProgressValue(unsigned int progress)
{
    ui->progressBarReceive->setValue(progress);
}

void BurnDlgDfu::SlotSetProgressRange(int min, int max)
{
    ui->progressBarReceive->setRange(min, max);
}

void BurnDlgDfu::changeEvent(QEvent* event)
{
    if (lastWinStates == windowState()) {
        QWidget::changeEvent(event);
        return;
    }
    if (windowState() == Qt::WindowMaximized) {
        lastWinStates = windowState();
        ui->tableView->setColumnWidth(TABLEVIEW_COLUMN_NAME, COLUMN_WIDTH_SIZE_300);
        ui->tableView->setColumnWidth(TABLEVIEW_COLUMN_PAHT, COLUMN_WIDTH_SIZE_500);
        ui->tableView->setMinimumHeight(COLUMN_MINIMUM_HEIGHT_SIZE_400);
    } else if (windowState() == Qt::WindowNoState) {
        lastWinStates = windowState();
        ui->tableView->setColumnWidth(TABLEVIEW_COLUMN_NAME, COLUMN_WIDTH_SIZE_100);
        ui->tableView->setColumnWidth(TABLEVIEW_COLUMN_PAHT, COLUMN_WIDTH_SIZE_200);
        ui->tableView->setMinimumHeight(COLUMN_MINIMUM_HEIGHT_SIZE_192);
    }
    QWidget::changeEvent(event);
}

void BurnDlgDfu::SlotUpdateList()
{
    UpdateList();
}

void BurnDlgDfu::SlotActionShow() const
{
    if (burnCtrl == nullptr || burnCtrl->IsBusy()) {
        ActionShowActEnable(false);
    } else {
        ActionShowActEnable(true);
    }
}

void BurnDlgDfu::ActionShowActEnable(bool enable) const
{
    if (actSettings == nullptr || actChinese == nullptr || actEnglish == nullptr) {
        return;
    }
    actSettings->setEnabled(enable);
    actChinese->setEnabled(enable);
    actEnglish->setEnabled(enable);
}

void BurnDlgDfu::SaveSettingCfg() const
{
    if (usbSetting == nullptr) {
        return;
    }

    unsigned int totalNum;
    unsigned int numPerLine;
    usbSetting->GetMultipleBurnParam(totalNum, numPerLine);
    burnCtrl->SetMultiBurnTotalNum(totalNum);
    burnCtrl->SetMultiBurnNumPerLine(numPerLine);
    if (cfg != nullptr) {
        cfg->setValue("USB_CONFIG/TOTAL_NUM", totalNum);
        cfg->setValue("USB_CONFIG/NUM_PER_LINE", numPerLine);
    }
}

void BurnDlgDfu::SlotBurnFileFinish()
{
    ui->btnStartBurn->setText(stringClass.GetStartBurn());
    ui->progressBarReceive->setValue(SHRT_MAX);
    if (chipType == ChipType::USB4GCAT1 || chipType == ChipType::USBHI3322) {
        SlotInsertPlainText(stringClass.GetUsbBurnMode() + "\n");
        SlotInsertPlainText(stringClass.GetEraseMode().arg(ui->cbEarseMode->currentText()) + "\n");
    }

    SlotInsertPlainText(IDS_STRING_32879 + "\n");
    SlotInsertPlainText(stringClass.GetIdsString32880() + "\n");
    SlotInsertPlainText(IDS_STRING_32879 + "\n");
}

void BurnDlgDfu::HidEnumerate(QMap <QString, HidDeviceInfo> &mapName)
{
    GUID interfaceClassGuid;
    CONFIGRET cr;
    CHAR* deviceInterfaceList = nullptr;
    DWORD len;
    HidD_GetHidGuid(&interfaceClassGuid);
    do {
        cr = CM_Get_Device_Interface_List_SizeA(&len, &interfaceClassGuid,
                                                nullptr, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
        if (cr != CR_SUCCESS) {
            BurnDlgDfu::SlotInsertPlainText("Failed to get size of HID device interface list. \n");
            break;
        }

        if (deviceInterfaceList != nullptr) {
            delete [] deviceInterfaceList;
            deviceInterfaceList = nullptr;
        }

        if (len <= 0) {
            printf("Memory is not compliant");
        }
        deviceInterfaceList = new CHAR[len]();
        if (deviceInterfaceList == nullptr) {
            BurnDlgDfu::SlotInsertPlainText("Failed to allocate memory for HID device interface list. \n");
            return;
        }
        cr = CM_Get_Device_Interface_ListA(&interfaceClassGuid, nullptr, deviceInterfaceList,
                                           len, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
        if (cr != CR_SUCCESS && cr != CR_BUFFER_SMALL) {
            BurnDlgDfu::SlotInsertPlainText("Failed to get HID device interface list. \n");
        }
    } while (cr == CR_BUFFER_SMALL);

    if (deviceInterfaceList == nullptr) {
        return;
    }
    if (cr != CR_SUCCESS) {
        delete [] deviceInterfaceList;
        deviceInterfaceList = nullptr;
        return;
    }

    for (CHAR* deviceDlgInterface = deviceInterfaceList; (*deviceDlgInterface) != 0;
         deviceDlgInterface += strlen(deviceDlgInterface) + 1) {
        HANDLE deviceHandle = INVALID_HANDLE_VALUE;
        HidDeviceInfo hidDeviceInfo;
        /* Open read-only handle to the device */
        deviceHandle = OpenDevice(deviceDlgInterface, FALSE);
        /* Check validity of device_handle. */
        if (deviceHandle == INVALID_HANDLE_VALUE) {
            /* Unable to open the device. */
            continue;
        }
        wchar_t string[MAX_STRING_WCHARS] = {0};
        QString productString = "";
        if (HidD_GetProductString(deviceHandle, string, MAX_STRING_WCHARS)) {
            string[MAX_STRING_WCHARS - 1] = 0;
            productString = QString::fromWCharArray(string);
            hidDeviceInfo.productName = productString;
        }
        HIDD_ATTRIBUTES attrib;

        if (HidD_GetAttributes(deviceHandle, &attrib)) {
            /* VID/PID */
            hidDeviceInfo.vid = attrib.VendorID;
            hidDeviceInfo.pid = attrib.ProductID;
        }
        PHIDP_PREPARSED_DATA data = nullptr;
        HIDP_CAPS caps;
        if (HidD_GetPreparsedData(deviceHandle, &data)) {
            if (HidP_GetCaps(data, &caps) == HIDP_STATUS_SUCCESS) {
                hidDeviceInfo.usagePage = caps.UsagePage;
                hidDeviceInfo.usage = caps.Usage;
            }
            mapName[QString::fromUtf8(deviceDlgInterface)] = hidDeviceInfo;
            HidD_FreePreparsedData(data);
        }
        CloseHandle(deviceHandle);
    }
    delete [] deviceInterfaceList;
    deviceInterfaceList = nullptr;
}

HANDLE BurnDlgDfu::OpenDevice(const CHAR *path, BOOL openRw)
{
    DWORD desiredAccess = (openRw) ? (GENERIC_WRITE | GENERIC_READ) : 0;
    DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
    HANDLE handle = CreateFileA(path, desiredAccess, shareMode, nullptr, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, nullptr);
    return handle;
}

void BurnDlgDfu::SlotDfuModeCurrentTextChanged(QString text)
{
    if (ui == nullptr) {
        return;
    }
    if (text == "Dfu") {
        dfuMode = DfuBurnMode::DFU;
        ui->comboBoxCom->setEnabled(true);
        ui->laPid->hide();
        ui->laVid->hide();
        ui->lePid->hide();
        ui->leVid->hide();
        ui->comboBoxCom->show();
        ui->labelCom->show();
    } else if (text == "Auto dfu") {
        dfuMode = DfuBurnMode::AUTO_DFU;
        ui->comboBoxCom->hide();
        ui->labelCom->hide();
        ui->comboBoxCom->setEnabled(false);
        ui->laPid->show();
        ui->laVid->show();
        ui->lePid->show();
        ui->leVid->show();
    } else if (text == "Hid dfu") {
        dfuMode = DfuBurnMode::HID_DFU;
        ui->comboBoxCom->hide();
        ui->labelCom->hide();
        ui->comboBoxCom->setEnabled(false);
        ui->laPid->show();
        ui->laVid->show();
        ui->lePid->show();
        ui->leVid->show();
    }
    if (burnCtrl != nullptr) {
        burnCtrl->SetIsAutoDfu(dfuMode);
    }
    SlotDfuSaveConfig();
}

void BurnDlgDfu::SlotBnClickedMultipleBurn()
{
    if (burnCtrl == nullptr) {
        return;
    }

    if (!isMultipleBurnOpen) {
        UpdateCmd();
        QMap<int, BIN_INFO> infoArray;
        GetCmdInfoArray(infoArray);
        if (infoArray.size() == 0) {
            QMessageBox::critical(this, stringClass.GetMessageError(),
                                  stringClass.GetIdsString33069(), stringClass.GetYes());
            return;
        }

        usbLocations.clear();
        unsigned int totalNum = cfg->value("USB_CONFIG/TOTAL_NUM", MULTIPLE_TOTAL_NUM).toUInt();
        for (unsigned int i = 0; i < totalNum; ++i) {
            QString location = cfg->value(QString("USB_LOCATION_CONFIG/LOCATION%1").arg(i + 1), "").toString();
            if (location.isEmpty()) {
                continue;
            }
            usbLocations.push_back(location);
        }

        if (static_cast<int>(totalNum) > usbLocations.size()) {
            QMessageBox::information(this, "warning", "配置文件中USB位置个数少于配置的工位个数，请检查!");
        }

        isMultipleBurnOpen = true;
        ui->btnStartBurn->setEnabled(false);
        FillParamAndShow(infoArray);
    }
}

void BurnDlgDfu::SlotMultipleBurnDlgClose()
{
    isMultipleBurnOpen = false;
    ui->btnStartBurn->setEnabled(true);
}

void BurnDlgDfu::FillParamAndShow(const QMap<int, BIN_INFO> &info)
{
    if (multipleBurnDlg != nullptr) {
        delete multipleBurnDlg;
        multipleBurnDlg = nullptr;
    }

    multipleBurnDlg = new MultipleBurnDlg();
    if (multipleBurnDlg == nullptr) {
        isMultipleBurnOpen = false;
        return;
    }

    multipleBurnDlg->SetChipType(chipType);
    multipleBurnDlg->SetAutoBurnFlag();
    multipleBurnDlg->SetInfoArray(info);
    int mode = ui->cbEarseMode->currentIndex();
    multipleBurnDlg->SetEraseAll(mode);
    connect(multipleBurnDlg, &MultipleBurnDlg::SignalClose, this, &BurnDlgDfu::SlotMultipleBurnDlgClose);
    multipleBurnDlg->SetCtrlNum(burnCtrl->GetMultiBurnTotalNum());
    multipleBurnDlg->SetOneLineNum(burnCtrl->GetMultiBurnNumPerLine());

    // 解析查找的USB设备中哪些满足VID和PID的要求
    bool ok = false;
    HidDeviceInfo hidInfo;
    hidInfo.vid = ui->leVid->text().toUInt(&ok, 0x10);
    hidInfo.pid = ui->lePid->text().toUInt(&ok, 0x10);
    multipleBurnDlg->SetUsbDeviceInfo(hidInfo);
    multipleBurnDlg->SetUsbLocations(usbLocations);

    multipleBurnDlg->InitUi();
    multipleBurnDlg->show();
}

void BurnDlgDfu::SlotEarseModeCurrentIndexChanged(int index)
{
    if (burnCtrl == nullptr || burnCtrl->IsBusy()) {
        QMessageBox::critical(this, stringClass.GetMessageError(),
                              stringClass.GetIdsString32984(), stringClass.GetYes());
        return;
    }

    eraseMode = index;
    burnCtrl->SetEraseMode(eraseMode);

    QMap<int, bool> mapCheck;
    QMap<int, BIN_INFO> mapBinInfo = binModel->GetMapBinFileInfo();
    for (int j = 0; j < mapBinInfo.size(); j++) {
        // 4GCAT1 USB烧写模式下erase all场景默认全选所有文件
        if (chipType == ChipType::USB4GCAT1 && eraseMode == 1) {
            mapCheck[j] = true;
        } else {
            if (mapBinInfo[j].type == IMAGE_TYPE::IMAGE_TYPE_RADIOCONFIG ||
                mapBinInfo[j].type == IMAGE_TYPE::IMAGE_TYPE_FLASHBOOT_3892 ||
                IsSelectOtp(mapBinInfo[j].type)) {
                mapCheck[j] = false;
            } else {
                mapCheck[j] = true;
            }
        }
    }
    binModel->FetchData(mapCheck);
    binModel->FetchData(mapBinInfo);

    LRESULT ret = 0;
    SlotNMKillfocusListBinPath(nullptr, &ret);

    if (cfg) {
        cfg->setValue("DFU_CONFIG/ERASE_MODE", ui->cbEarseMode->currentText());
    }
}

void BurnDlgDfu::LoadDlgConfig() const
{
    if (cfg == nullptr) {
        return;
    }

    unsigned int totalNum = cfg->value("USB_CONFIG/TOTAL_NUM", MULTIPLE_TOTAL_NUM).toUInt();
    unsigned int numPerLine = cfg->value("USB_CONFIG/NUM_PER_LINE", MULTIPLE_NUM_PER_LINE).toUInt();

    if (usbSetting != nullptr) {
        usbSetting->SetMultipleBurnParam(totalNum, numPerLine);
        usbSetting->LoadDlgConfig();
    }

    if (burnCtrl != nullptr) {
        burnCtrl->SetMultiBurnTotalNum(totalNum);
        burnCtrl->SetMultiBurnNumPerLine(numPerLine);
    }
}

